// 2013年版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 此文件实现类型的打印。

package types2

import (
	"bytes"
	"fmt"
	"unicode/utf8"
)

// 限定符控制如何在中打印命名的包级别对象
// 调用TypeString、ObjectString和SelectionString。
// None
// 这三个格式化例程分别调用限定符
// 包级对象O，如果限定符返回非空
// 字符串p，对象以p.O的形式打印。
// 如果返回空字符串，则只打印对象名O。
// None
// 使用nil限定符相当于使用（*Package）
// 对象由导入路径限定，例如“encoding/json.Marshal”。
// None
type Qualifier func(*Package) string

// RelativeTo返回完全限定
// 除包装外的所有包装。
func RelativeTo(pkg *Package) Qualifier {
	if pkg == nil {
		return nil
	}
	return func(other *Package) string {
		if pkg == other {
			return "" // 同一包装；不合格的
		}
		return other.Path()
	}
}

// 如果设置了gcCompatibilityMode，则会修改类型的打印
// 要匹配gc编译器中某些类型的表示形式，请执行以下操作：
// None
// -字节和符文失去了别名，只是代表
// 分别为uint8和int32
// -嵌入接口变平（嵌入信息丢失，
// 并且某些递归接口类型无法再打印）
// None
// 这使得比较使用该类型计算的包更加容易-
// 检查器与从gc导出数据导入的包。
// None
// 警告：此标志全局影响WriteType的所有使用。
// 它仅用于与测试一起提供
// gc生成的数据。
// None
// 此标志在x/tools/go/types包中导出。我们没有
// 在std回购协议中需要它，所以我们不出口它
// 不再我们最终应该尝试将其完全移除。
// TODO（gri）删除此
var gcCompatibilityMode bool

// TypeString返回类型的字符串表示形式。
// 限定符控制打印
// 包级对象，并且可以为零。
func TypeString(typ Type, qf Qualifier) string {
	var buf bytes.Buffer
	WriteType(&buf, typ, qf)
	return buf.String()
}

// WriteType将typ的字符串表示形式写入buf。
// 限定符控制打印
// 包级对象，并且可以为零。
func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
	writeType(buf, typ, qf, make([]Type, 0, 8))
}

// instanceMarker是实例化类型的前缀
// 以“非评估”实例形式。
const instanceMarker = '#'

func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
	// 理论上，这是一种二次查找算法，但在
	// 使用未命名组件练习深度嵌套的复合类型
	// 类型不常见。此代码可能比
	// 使用地图。
	for _, t := range visited {
		if t == typ {
			fmt.Fprintf(buf, "○%T", goTypeName(typ)) // 循环到典型
			return
		}
	}
	visited = append(visited, typ)

	switch t := typ.(type) {
	case nil:
		buf.WriteString("<nil>")

	case *Basic:
		// 导出的基本类型进入包中
		// （目前这只是不安全的。指针）
		if isExported(t.name) {
			if obj, _ := Unsafe.scope.Lookup(t.name).(*TypeName); obj != nil {
				writeTypeName(buf, obj, qf)
				break
			}
		}

		if gcCompatibilityMode {
			// 忘记别名吧
			switch t.kind {
			case Byte:
				t = Typ[Uint8]
			case Rune:
				t = Typ[Int32]
			}
		}
		buf.WriteString(t.name)

	case *Array:
		fmt.Fprintf(buf, "[%d]", t.len)
		writeType(buf, t.elem, qf, visited)

	case *Slice:
		buf.WriteString("[]")
		writeType(buf, t.elem, qf, visited)

	case *Struct:
		buf.WriteString("struct{")
		for i, f := range t.fields {
			if i > 0 {
				buf.WriteString("; ")
			}
			// 对于嵌入式类型，这不是正确的做法
			// 我们应该打印别名的别名，而不是
			// 别名类型（见第44410期）。
			if !f.embedded {
				buf.WriteString(f.name)
				buf.WriteByte(' ')
			}
			writeType(buf, f.typ, qf, visited)
			if tag := t.Tag(i); tag != "" {
				fmt.Fprintf(buf, " %q", tag)
			}
		}
		buf.WriteByte('}')

	case *Pointer:
		buf.WriteByte('*')
		writeType(buf, t.base, qf, visited)

	case *Tuple:
		writeTuple(buf, t, false, qf, visited)

	case *Signature:
		buf.WriteString("func")
		writeSignature(buf, t, qf, visited)

	case *Sum:
		for i, t := range t.types {
			if i > 0 {
				buf.WriteString(", ")
			}
			writeType(buf, t, qf, visited)
		}

	case *Interface:
		// 我们编写源代码级方法和嵌入类型，而不是
		// 而不是自解析方法签名以来设置的实际方法
		// 如果参数已嵌入，则可能具有不可打印的循环
		// （直接或间接）嵌入
		// 当前接口。例如，考虑结果类型
		// 关于m：
		// None
		// T型接口{
		// m（）接口{T}
		// }
		// None
		buf.WriteString("interface{")
		empty := true
		if gcCompatibilityMode {
			// 打印平坦界面
			// （用于与gc生成的接口进行比较）
			for i, m := range t.allMethods {
				if i > 0 {
					buf.WriteString("; ")
				}
				buf.WriteString(m.name)
				writeSignature(buf, m.typ.(*Signature), qf, visited)
				empty = false
			}
			if !empty && t.allTypes != nil {
				buf.WriteString("; ")
			}
			if t.allTypes != nil {
				buf.WriteString("type ")
				writeType(buf, t.allTypes, qf, visited)
			}
		} else {
			// 打印显式接口方法和嵌入类型
			for i, m := range t.methods {
				if i > 0 {
					buf.WriteString("; ")
				}
				buf.WriteString(m.name)
				writeSignature(buf, m.typ.(*Signature), qf, visited)
				empty = false
			}
			if !empty && t.types != nil {
				buf.WriteString("; ")
			}
			if t.types != nil {
				buf.WriteString("type ")
				writeType(buf, t.types, qf, visited)
				empty = false
			}
			if !empty && len(t.embeddeds) > 0 {
				buf.WriteString("; ")
			}
			for i, typ := range t.embeddeds {
				if i > 0 {
					buf.WriteString("; ")
				}
				writeType(buf, typ, qf, visited)
				empty = false
			}
		}
		if t.allMethods == nil || len(t.methods) > len(t.allMethods) {
			if !empty {
				buf.WriteByte(' ')
			}
			buf.WriteString("/* incomplete */")
		}
		buf.WriteByte('}')

	case *Map:
		buf.WriteString("map[")
		writeType(buf, t.key, qf, visited)
		buf.WriteByte(']')
		writeType(buf, t.elem, qf, visited)

	case *Chan:
		var s string
		var parens bool
		switch t.dir {
		case SendRecv:
			s = "chan "
			// chan（<-chan T）需要括号
			if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
				parens = true
			}
		case SendOnly:
			s = "chan<- "
		case RecvOnly:
			s = "<-chan "
		default:
			panic("unreachable")
		}
		buf.WriteString(s)
		if parens {
			buf.WriteByte('(')
		}
		writeType(buf, t.elem, qf, visited)
		if parens {
			buf.WriteByte(')')
		}

	case *Named:
		writeTypeName(buf, t.obj, qf)
		if t.targs != nil {
			// 实例化类型
			buf.WriteByte('[')
			writeTypeList(buf, t.targs, qf, visited)
			buf.WriteByte(']')
		} else if t.tparams != nil {
			// 参数化类型
			writeTParamList(buf, t.tparams, qf, visited)
		}

	case *TypeParam:
		s := "?"
		if t.obj != nil {
			s = t.obj.name
		}
		buf.WriteString(s + subscript(t.id))

	case *instance:
		buf.WriteByte(instanceMarker) // 表示“未评估”语法实例
		writeTypeName(buf, t.base.obj, qf)
		buf.WriteByte('[')
		writeTypeList(buf, t.targs, qf, visited)
		buf.WriteByte(']')

	case *bottom:
		buf.WriteString("⊥")

	case *top:
		buf.WriteString("⊤")

	default:
		// 对于类型的外部定义实现。
		buf.WriteString(t.String())
	}
}

func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
	for i, typ := range list {
		if i > 0 {
			buf.WriteString(", ")
		}
		writeType(buf, typ, qf, visited)
	}
}

func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
	buf.WriteString("[")
	var prev Type
	for i, p := range list {
		// TODO（gri）支持“任意”糖。
		var b Type = &emptyInterface
		if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil {
			b = t.bound
		}
		if i > 0 {
			if b != prev {
				// 类型绑定已更改-在前进之前写入上一个
				buf.WriteByte(' ')
				writeType(buf, prev, qf, visited)
			}
			buf.WriteString(", ")
		}
		prev = b

		if t, _ := p.typ.(*TypeParam); t != nil {
			writeType(buf, t, qf, visited)
		} else {
			buf.WriteString(p.name)
		}
	}
	if prev != nil {
		buf.WriteByte(' ')
		writeType(buf, prev, qf, visited)
	}
	buf.WriteByte(']')
}

func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
	s := "<Named w/o object>"
	if obj != nil {
		if obj.pkg != nil {
			writePackage(buf, obj.pkg, qf)
		}
		// TODO（gri）：应显示函数本地命名类型
		// 与包级别的命名类型不同，以避免
		// 模棱两可
		s = obj.name
	}
	buf.WriteString(s)
}

func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
	buf.WriteByte('(')
	if tup != nil {
		for i, v := range tup.vars {
			if i > 0 {
				buf.WriteString(", ")
			}
			if v.name != "" {
				buf.WriteString(v.name)
				buf.WriteByte(' ')
			}
			typ := v.typ
			if variadic && i == len(tup.vars)-1 {
				if s, ok := typ.(*Slice); ok {
					buf.WriteString("...")
					typ = s.elem
				} else {
					// 特殊情况：
					// 追加（s，“foo”…）导致签名func（[]字节，字符串…）
					if t := asBasic(typ); t == nil || t.kind != String {
						panic("internal error: string type expected")
					}
					writeType(buf, typ, qf, visited)
					buf.WriteString("...")
					continue
				}
			}
			writeType(buf, typ, qf, visited)
		}
	}
	buf.WriteByte(')')
}

// WriteSignature将签名sig的表示形式写入buf，
// 没有前导的“func”关键字。
// 限定符控制打印
// 包级对象，并且可以为零。
func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
	writeSignature(buf, sig, qf, make([]Type, 0, 8))
}

func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
	if sig.tparams != nil {
		writeTParamList(buf, sig.tparams, qf, visited)
	}

	writeTuple(buf, sig.params, sig.variadic, qf, visited)

	n := sig.results.Len()
	if n == 0 {
		// 没有结果
		return
	}

	buf.WriteByte(' ')
	if n == 1 && sig.results.vars[0].name == "" {
		// 单个未命名结果
		writeType(buf, sig.results.vars[0].typ, qf, visited)
		return
	}

	// 多个或命名的结果
	writeTuple(buf, sig.results, false, qf, visited)
}

// 下标使用下标数字返回x的十进制（utf8）表示形式。
func subscript(x uint64) string {
	const w = len("₀") // 所有数字0…9具有相同的utf8宽度
	var buf [32 * w]byte
	i := len(buf)
	for {
		i -= w
		utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // None
		x /= 10
		if x == 0 {
			break
		}
	}
	return string(buf[i:])
}
